package com.sky.config;

import com.sky.interceptor.JwtTokenAdminInterceptor;
import com.sky.interceptor.JwtTokenUserInterceptor;
import com.sky.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;

/**
 * 配置类，注册web层相关组件
 * 这个配置类是和swagger(用于生成接口文档)和knife4j生成Api文档相关的配置类
 * 拦截器好像都要在这里注册
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired  //自动注入,将下面对应的类对象,注入到当前类中
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

    @Autowired        //把我们写好的用户端拦截器注入进来
    private JwtTokenUserInterceptor jwtTokenUserInterceptor;

    /**
     * 注册自定义拦截器
     * 配置类好像就是在 主程序启动的时候,,就会自动调用,,忘了回头看看spring_yue笔记
     * 配置类:  这意味着该类可以使用 @Bean 注解来定义 bean。那就是说专门用于注入到spring容器的
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)       //注册我们自定义的商家端的拦截器
                .addPathPatterns("/admin/**")     //这里拦截的表示,是以admin开头的请求
                .excludePathPatterns("/admin/employee/login");   //这里表示要排除的方法,不拦截
        //也就是说你/admin这个路径下的所有的请求都会被拦截,但是你登陆,就不拦截,你得去登录
            //目的也就是验证这个jwt令牌

        //下面把用户端自定义的拦截器,也需要注册下
        registry.addInterceptor(jwtTokenUserInterceptor)       //注册我们自定义的商家端的拦截器
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");
                    //这个路径也是用户端的,也要排除,商家店铺打不打样,就不用jwt令牌验证了;
                        //因为我们在最开始进入这个小程序的时候,,首先就会获取当前的营业状态;即在登录之前就会请求这个路径

    }

    /**
     * 通过knife4j生成接口文档
     * 下面的会改下面的扫描路径啥的就可以,就是一个配置信息;  不用完全手写代码,不用去了解具体的机制和方法
     * 对于生成的Api接口文档,对应的前端页面的信息,比如下面的标题"苍穹外卖项目接口文档"等等,不理解看视频Day01_13
     * @return
     */
    @Bean  //springboot提供的,可以直接注入到容器中
    public Docket docket1() {
        log.info("准备生成接口文档...");
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("管理端接口")      //为了生成两份接口文档,所以这里设置了一个分组
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")) //指定我们要扫描的包
                //如果上面这个扫描的包我们写错了,,那就扫描不到,那对应的controller层的方法就无法生成对应的Api接口文档
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    //下面这个补充的方法docket2和上面docket1一样,只是为了生成接口文档的时候生成两个,即把商家管理端和用户端区分开
        //主要就是下面扫描包的路径,改了一下原本默认是"com.sky.controller",现在详细的区分开了;;相当于再分别扫描一次
    @Bean  //springboot提供的,可以直接注入到容器中
    public Docket docket2() {
        log.info("准备生成接口文档...");
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("用户端接口")      //为了生成两份接口文档,所以这里设置了一个分组
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller.user")) //指定我们要扫描的包
                //如果上面这个扫描的包我们写错了,,那就扫描不到,那对应的controller层的方法就无法生成对应的Api接口文档
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    /**
     * 设置静态资源映射
     * @param registry
     * 下面的方法名不能乱写,addResourceHandlers这个方法是重写的WebMvcConfigurationSupport这个父类中的方法
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    //如果不设置这个静态资源映射(可以先注释掉,重启项目来测试),网页就会报404的错误
    //注释:  如果不设置静态资源映射,那它访问/doc.html,spring框架就会认为访问的可能是controller对应的url;
        //而不会识别成一种静态资源,就是脑袋里有一点印象,是不是当初重定向跳转方法,以及跳转到前端页面也涉及到过这个知识点
        //还有,这里能清晰的认识到什么是静态资源,那就是可以通过访问绝对路径,直接可以访问的前端页面,
            //而是不是controller对应的url映射路径,这个是请求调用对应的方法

    /**
     * 扩展Spring MVC框架的消息转换器------统一对我们后端返回给前端的数据统一进行转换处理
     * 这里,就是用来日期格式化的第2种方式----老师推荐这种方式,对应讲义day02_2.4代码完善
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展我们消息转换器...");   //这个日志输出,用来测试用,来看这个方法是否起作用
        //自己创建的消息转换器对象--(也是框架提供的,直接用)
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //JacksonObjectMapper这个对象转换器(在sky-common对应的json目录下),
            // 老师说都是固定的代码直接用,不用去底层分析具体的实现,直接new一个实例

        //将我们上面弄好的converter加入到框架的消息转换器中,也就是converters(框架的所有消息转换器都在这个list集合中)
        //或者通俗的理解:  将自己的消息转换器加入到容器中
        converters.add(0,converter);
        //前面这个参数0,就表示优先级最高了;
        // 要不默认情况下加入到容器中,就会排到最后,那优先用框架自己提供的,就不会我们自定义的这个消息转换器


    }

}
